Voici 4 scripts C sharp afin d'établir un système "laser in dotted lines" rotatif:
DirectionPrefab.cs
Code : Tout sélectionner
using UnityEngine;
using System; // Required for [Serializable]
// DirectionPrefab.cs
[Serializable]
public class DirectionPrefab
{
[Tooltip("The base direction for the tube emitted from the multi-source (relative to spawner's forward).")]
public Vector3 direction;
[Tooltip("The specific tube prefab to use for this direction.")]
public GameObject tubePrefab;
}
Code : Tout sélectionner
using UnityEngine;
// TubeInfoRotateConique.cs
public class TubeInfoRotateConique
{
public GameObject tubeGameObject;
public Vector3 baseRelativeDirection; // Direction relative to the spawner's local space
public Vector3 currentAbsoluteDirection; // World space direction after spawner's rotation
public float speed;
public int currentBounces;
public int maxBouncesLimit;
public int recursionLevel;
public Vector3 initialSpawnPosition;
public bool hasReflected;
private float _distanceTraveled;
public TubeInfoRotateConique(GameObject go, Vector3 baseDir, float spd, int bnc, int maxBnc, int recLevel, Vector3 spawnPos)
{
tubeGameObject = go;
baseRelativeDirection = baseDir;
currentAbsoluteDirection = Vector3.zero;
speed = spd;
currentBounces = bnc;
maxBouncesLimit = maxBnc;
recursionLevel = recLevel;
initialSpawnPosition = spawnPos;
hasReflected = false;
_distanceTraveled = 0f;
}
public void UpdateDistance(float deltaTime)
{
_distanceTraveled += speed * deltaTime;
}
public Vector3 GetWorldPosition(Transform spawnerTransform)
{
// Calculate world position based on spawner's rotation and position
Vector3 localPoint = baseRelativeDirection.normalized * _distanceTraveled;
return spawnerTransform.TransformPoint(localPoint);
}
public Vector3 GetWorldDirection(Transform spawnerTransform)
{
// Calculate world direction based on spawner's rotation
return spawnerTransform.TransformDirection(baseRelativeDirection.normalized);
}
}
Code : Tout sélectionner
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MultiLaserSpawnerRotateConique : MonoBehaviour
{
// --- Configuration générale des tubes (reçue de LaserInfinyRotateConique) ---
private float _speed;
private float _spawnInterval;
private float _laserLength;
// --- Référence au script principal LaserInfinyRotateConique ---
private LaserInfinyRotateConique _mainLaserScript;
private int _currentRecursionLevel;
// --- Liste des tubes actuellement actifs pour ce spawner ---
private List<TubeInfoRotateConique> _spawnedTubes = new List<TubeInfoRotateConique>();
// --- État d'initialisation du spawner ---
private bool _isInitialized = false;
// Paramètres pour la rotation longitudinale (Lacet) continue
private bool _enableContinuousLongitudinalRotation;
private float _continuousLongitudinalRotationSpeed;
private float _currentContinuousLongitudeAngle = 0f; // Angle de lacet actuel pour la rotation continue
// Paramètres pour l'Oscillation Angulaire Calculée
private bool _enableAngularOscillation;
private float _oscillationSpeed; // Vitesse angulaire de l'oscillation en degrés par seconde
private Quaternion _oscillationTargetRotation; // La rotation FromTo (du début à la fin de l'oscillation)
private float _oscillationAngularMagnitude; // L'amplitude angulaire réelle de l'oscillation en degrés
private float _oscillationTimer = 0f; // Compteur pour la fonction d'oscillation PingPong
/// <summary>
/// Initialise le MultiLaserSpawnerRotateConique avec les paramètres nécessaires.
/// </summary>
public void Init(
LaserInfinyRotateConique mainScript,
float spd,
float interval,
Vector3 initialPos,
int recLevel,
bool enableLongRot,
float longRotSpeed,
bool enableAngularOsc, // Indique si l'oscillation angulaire est activée
float oscSpeedPerSec, // Vitesse de l'oscillation en deg/sec
Quaternion oscTargetRot // La rotation cible pour l'oscillation
)
{
_mainLaserScript = mainScript;
_speed = spd;
_spawnInterval = interval;
_currentRecursionLevel = recLevel;
_laserLength = mainScript.laserLength;
transform.position = initialPos;
_currentContinuousLongitudeAngle = 0f; // Le lacet continu démarre à 0
_enableContinuousLongitudinalRotation = enableLongRot;
_continuousLongitudinalRotationSpeed = longRotSpeed;
_enableAngularOscillation = enableAngularOsc;
_oscillationSpeed = oscSpeedPerSec; // Stocke la vitesse en deg/sec
_oscillationTargetRotation = oscTargetRot;
if (_enableAngularOscillation)
{
// Calcule l'amplitude angulaire totale de la rotation d'oscillation
_oscillationAngularMagnitude = Quaternion.Angle(Quaternion.identity, _oscillationTargetRotation);
// Si l'amplitude angulaire est très petite, désactiver l'oscillation
// pour éviter une division par zéro ou un mouvement insignifiant et potentiellement très rapide.
if (_oscillationAngularMagnitude < 0.01f) // Utilisez un petit seuil (0.01 degrés)
{
_enableAngularOscillation = false; // Désactive l'oscillation pour ce spawner
Debug.LogWarning($"[MultiLaserSpawnerRotateConique] Oscillation désactivée pour ce spawner car le déplacement angulaire cible est trop petit ({_oscillationAngularMagnitude:F4} degrés).");
}
}
else
{
_oscillationAngularMagnitude = 0f; // S'assurer que la magnitude est 0 si l'oscillation est désactivée
}
// Commence à faire apparaître les tubes de manière répétée
// InvokeRepeating(nameof(SpawnTube), 0f, _spawnInterval);//avant
InvokeRepeating(nameof(SpawnTube), 0.001f, _spawnInterval); // Ajout d'un petit délai initial pour la synchronisation
_isInitialized = true;
Debug.Log($"[MultiLaserSpawnerRotateConique] Initialisé à {transform.position}. Spawning tubes every {_spawnInterval} seconds. Recursion Level: {_currentRecursionLevel}.");
Debug.Log($"[MultiLaserSpawnerRotateConique] Rotation: Longitudinale Continue Activée={_enableContinuousLongitudinalRotation} (Vitesse: {_continuousLongitudinalRotationSpeed}). Oscillation Angulaire Activée={_enableAngularOscillation} (Vitesse: {_oscillationSpeed:F2} deg/sec, Magnitude Angulaire: {_oscillationAngularMagnitude:F2} deg).");
}
/// <summary>
/// Crée un nouveau tube à partir de ce spawner.
/// </summary>
void SpawnTube()
{
if (!_isInitialized)
{
Debug.LogWarning("[MultiLaserSpawnerRotateConique] Tentative de 'SpawnTube' avant l'initialisation. Le spawner n'est pas initialisé.");
return;
}
Vector3 initialBaseDirection;
GameObject currentTubePrefab;
if (_mainLaserScript.directionPrefabs != null && _mainLaserScript.directionPrefabs.Length > 0)
{
// Cycle à travers les préfabriqués de direction en fonction du nombre de tubes apparus par CE spawner
int index = (_spawnedTubes.Count) % _mainLaserScript.directionPrefabs.Length;
LaserInfinyRotateConique.DirectionPrefab dp = _mainLaserScript.directionPrefabs[index]; // Notez le 'LaserInfinyRotateConique.' ici
// --- UTILISATION DE CartesianDirection ICI ---
initialBaseDirection = dp.CartesianDirection;
currentTubePrefab = dp.tubePrefab;
if (currentTubePrefab == null)
{
Debug.LogError($"[MultiLaserSpawnerRotateConique] Le préfabriqué de tube est null pour l'index de direction {index} dans LaserInfinyRotateConique. Veuillez l'assigner dans l'Inspector.");
CancelInvoke(nameof(SpawnTube)); // Arrête l'apparition si le préfabriqué est manquant
return;
}
}
else
{
Debug.LogError("[MultiLaserSpawnerRotateConique] _mainLaserScript.directionPrefabs est null ou vide. Impossible de faire apparaître le tube. Assurez-vous qu'il est configuré dans LaserInfinyRotateConique.");
CancelInvoke(nameof(SpawnTube)); // Arrête l'apparition s'il n'y a pas de préfabriqués de direction
return;
}
Vector3 spawnPosition = transform.position;
GameObject tubeGO = Instantiate(currentTubePrefab, spawnPosition, Quaternion.identity);
// Crée un nouvel objet TubeInfoRotateConique pour gérer le tube apparu
TubeInfoRotateConique newTube = new TubeInfoRotateConique(tubeGO, initialBaseDirection, _speed, 0, 0, _currentRecursionLevel, spawnPosition);
_spawnedTubes.Add(newTube);
}
/// <summary>
/// Appelé une fois par frame. Manages spawner rotation and tube movement/destruction.
/// </summary>
void Update()
{
if (!_isInitialized) return;
// Commence avec la rotation d'identité pour accumuler les rotations
Quaternion totalRotation = Quaternion.identity;
// --- Applique la rotation longitudinale (Lacet) continue si activée ---
if (_enableContinuousLongitudinalRotation)
{
_currentContinuousLongitudeAngle += _continuousLongitudinalRotationSpeed * Time.deltaTime;
_currentContinuousLongitudeAngle = Mathf.Repeat(_currentContinuousLongitudeAngle, 360f); // Garde l'angle entre 0-360
totalRotation *= Quaternion.Euler(0, _currentContinuousLongitudeAngle, 0);
}
// --- Applique l'oscillation angulaire calculée si activée ---
if (_enableAngularOscillation)
{
// Calcul de la progression du timer pour que l'oscillation ait une vitesse angulaire constante.
// Le timer PingPong varie de 0 à 1 puis de 1 à 0, pour une durée totale de 2 unités de timer par cycle.
// La vitesse _oscillationSpeed est en degrés/sec. L'amplitude totale est _oscillationAngularMagnitude (deg).
// Le temps pour parcourir l'amplitude dans un sens est _oscillationAngularMagnitude / _oscillationSpeed.
// Le timer doit progresser de 1.0f / (temps pour un sens) par seconde.
_oscillationTimer += (_oscillationSpeed / _oscillationAngularMagnitude) * Time.deltaTime;
// normalizedProgress ira de 0.0 (début) à 1.0 (fin de course), puis reviendra à 0.0
float normalizedProgress = Mathf.PingPong(_oscillationTimer, 1.0f);
// Slerp interpole entre la rotation d'identité (point de départ)
// et la rotation cible calculée (_oscillationTargetRotation)
Quaternion currentOscillationRotation = Quaternion.Slerp(Quaternion.identity, _oscillationTargetRotation, normalizedProgress);
// Applique la rotation d'oscillation en plus des rotations précédentes
totalRotation *= currentOscillationRotation;
}
// Applique la rotation totale au spawner
transform.localRotation = totalRotation;
// --- Débogage Visuel pour l'orientation du spawner ---
Debug.DrawRay(transform.position, transform.forward * 5f, Color.yellow, 0.1f);
Debug.DrawRay(transform.position, transform.up * 5f, Color.cyan, 0.1f);
// --- Gère le Mouvement et la Destruction des Tubes ---
for (int k = _spawnedTubes.Count - 1; k >= 0; k--)
{
TubeInfoRotateConique tube = _spawnedTubes[k];
// Vérifie si l'objet tube ou les informations sont devenus null inopinément
if (tube == null || tube.tubeGameObject == null)
{
Debug.Log($"[MultiLaserSpawnerRotateConique] TubeInfoRotateConique ou GameObject à l'index {k} était null, suppression de la liste.");
_spawnedTubes.RemoveAt(k);
continue;
}
tube.UpdateDistance(Time.deltaTime); // Met à jour la distance parcourue
tube.tubeGameObject.transform.position = tube.GetWorldPosition(transform); // Met à jour la position mondiale du tube
tube.tubeGameObject.transform.up = tube.GetWorldDirection(transform); // Oriente le tube le long de sa direction
tube.currentAbsoluteDirection = tube.GetWorldDirection(transform); // Stocke la direction mondiale actuelle
// Détruit le tube s'il dépasse la limite de longueur du laser
if (Vector3.Distance(tube.tubeGameObject.transform.position, tube.initialSpawnPosition) > _laserLength * 1.5f)
{
Debug.Log($"[MultiLaserSpawnerRotateConique] Le tube '{tube.tubeGameObject.name}' a dépassé la longueur maximale du laser, destruction.");
Destroy(tube.tubeGameObject);
_spawnedTubes.RemoveAt(k); // Supprime de la liste
}
}
// Détruit le spawner lui-même s'il n'a plus de tubes et ne fait plus apparaître
if (_isInitialized && _spawnedTubes.Count == 0 && !IsInvoking(nameof(SpawnTube)))
{
Debug.Log($"[MultiLaserSpawnerRotateConique] Tous les tubes de ce spawner ont été détruits et aucune apparition n'est plus programmée. Destruction de l'objet GameObject du spawner : {gameObject.name}.");
Destroy(gameObject);
}
}
/// <summary>
/// Appelé lorsque le GameObject est désactivé ou détruit.
/// Annule toutes les invocations restantes et détruit les tubes apparus pour éviter les fuites.
/// </summary>
void OnDisable()
{
CancelInvoke(); // Arrête tous les appels InvokeRepeating en attente
// Détruit tous les tubes apparus par ce spawner
foreach (var tubeInfo in _spawnedTubes)
{
if (tubeInfo != null && tubeInfo.tubeGameObject != null)
{
Destroy(tubeInfo.tubeGameObject);
}
}
_spawnedTubes.Clear(); // Vide la liste
}
}
Code : Tout sélectionner
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class LaserInfinyRotateConique : MonoBehaviour
{
[Header("Paramètres Généraux du Laser")]
public float speed = 10f;
public float laserLength = 50f;
[Header("Intervalles d'Apparition")]
[Tooltip("Intervalles d'apparition spécifiques pour les lasers multi-sources basés sur leur index (facultatif).")]
public float[] intervalSpawns;
// --- NOUVELLE DÉFINITION DES DIRECTIONS EN COORDONNÉES POLAIRES (SPHÉRIQUES) ---
[System.Serializable]
public class DirectionPrefab
{
[Tooltip("La latitude de la direction en degrés (angle par rapport au plan XZ). -90° est directement vers le bas, 0° est dans le plan XZ, 90° est directement vers le haut.")]
[Range(-90f, 90f)]
public float latitude; // En degrés
[Tooltip("La longitude de la direction en degrés (angle dans le plan XZ, de l'axe Z+ vers l'axe X+). 0° est vers Z+, 90° vers X+, 180° vers Z-, -90° vers X- (ou 270°).")]
[Range(-180f, 180f)] // Plage commune pour une couverture complète
public float longitude; // En degrés
[Tooltip("Le préfabriqué du tube de laser à instancier pour cette direction.")]
public GameObject tubePrefab;
// Propriété pour obtenir la direction cartésienne normalisée à partir des angles polaires
public Vector3 CartesianDirection
{
get
{
// Convertir les degrés en radians
float latRad = latitude * Mathf.Deg2Rad;
float longRad = longitude * Mathf.Deg2Rad;
// Calcul des composantes cartésiennes (pour un vecteur unitaire)
float x = Mathf.Cos(latRad) * Mathf.Sin(longRad);
float y = Mathf.Sin(latRad);
float z = Mathf.Cos(latRad) * Mathf.Cos(longRad);
// Retourne le vecteur normalisé (devrait déjà être unitaire si la trigonométrie est correcte)
return new Vector3(x, y, z).normalized;
}
}
}
[Header("Directions de Départ & Préfabriqués")]
[Tooltip("Définit les directions de base (en coordonnées polaires) et leurs préfabriqués de tubes associés. Cycles si plus de lasers que de définitions.")]
public DirectionPrefab[] directionPrefabs;
[Header("Rotation Longitudinale (Lacet)")]
[Tooltip("Active la rotation longitudinale continue (autour de l'axe Y local / lacet) pour toutes les multi-sources apparues.")]
public bool enableLongitudinalRotation = true;
[Tooltip("Vitesse de la rotation longitudinale continue en degrés par seconde.")]
public float longitudinalRotationSpeed = 60f;
[Header("Oscillation Angulaire Calculée")]
[Tooltip("Active l'oscillation angulaire calculée automatiquement.")]
public bool enableAngularOscillation = false;
[Tooltip("Vitesse angulaire moyenne de l'oscillation en degrés par seconde. Une valeur plus élevée rend l'oscillation plus rapide, comme la rotation longitudinale.")]
public float oscillationSpeed = 60f; // Défaut à 60 deg/sec pour cohérence
private List<MultiLaserSpawnerRotateConique> _activeMultiSourceSpawners = new List<MultiLaserSpawnerRotateConique>();
void Start()
{
if (directionPrefabs == null || directionPrefabs.Length == 0)
{
Debug.LogError("[LaserInfinyRotateConique] Les 'directionPrefabs' ne sont pas définis ! Veuillez assigner au moins un 'DirectionPrefab' dans l'Inspector.");
enabled = false;
return;
}
bool anyPrefabAssigned = false;
for (int i = 0; i < directionPrefabs.Length; i++)
{
DirectionPrefab dp = directionPrefabs[i];
if (dp != null && dp.tubePrefab != null)
{
anyPrefabAssigned = true;
Quaternion oscillationTargetRotationForThisDP = Quaternion.identity; // Rotation d'oscillation par défaut (aucune)
bool isValidOscillationForThisPrefab = true; // Indicateur si l'oscillation est valide pour ce préfabriqué
if (enableAngularOscillation)
{
// --- UTILISATION DE CartesianDirection ICI ---
Vector3 startDirection = dp.CartesianDirection; // La direction de base convertie
Vector3 endDirection = new Vector3(0, dp.CartesianDirection.y, 0); // La direction cible (0, b, 0)
// Normalisation des directions pour le calcul de la rotation
if (startDirection.sqrMagnitude > 0.0001f) // Vérifie que la direction de départ n'est pas un vecteur nul
{
startDirection.Normalize();
}
else
{
Debug.LogWarning($"[LaserInfinyRotateConique] DirectionPrefab[{i}] a une direction de départ nulle (latitude ou longitude invalide pour un vecteur unitaire). L'oscillation angulaire sera désactivée pour ce préfabriqué.");
isValidOscillationForThisPrefab = false;
}
if (isValidOscillationForThisPrefab)
{
if (endDirection.sqrMagnitude < 0.0001f) // Vérifie si la direction d'arrivée est un vecteur nul
{
Debug.LogWarning($"[LaserInfinyRotateConique] DirectionPrefab[{i}] a une direction d'arrivée cible (0,Y,0) où Y=0. Oscillating vers (0,0,0) n'est pas une oscillation directionnelle valide. L'oscillation angulaire sera désactivée pour ce préfabriqué.");
isValidOscillationForThisPrefab = false;
}
else
{
endDirection.Normalize(); // Normalise la direction d'arrivée
// Calcule la rotation nécessaire pour passer de la direction de départ à la direction d'arrivée.
oscillationTargetRotationForThisDP = Quaternion.FromToRotation(startDirection, endDirection);
Debug.Log($"[LaserInfinyRotateConique] Direction[{i}] (Polar: Lat={dp.latitude:F2} Lon={dp.longitude:F2}, Cartesian: {dp.CartesianDirection.ToString("F3")}): " +
$"Oscillation calculée de {startDirection.ToString("F3")} à {endDirection.ToString("F3")}");
}
}
}
// Crée le nouveau spawner et lui passe les paramètres d'oscillation calculés.
CreateNewMultiSourceSpawner(
transform.position,
i, // Utilise l'index actuel pour le prefab (cycle automatique par % dans le spawner)
0, // Niveau de récursion initial
0f, // Délai
enableAngularOscillation && isValidOscillationForThisPrefab, // Active l'oscillation uniquement si valide
oscillationSpeed, // Passe la vitesse en deg/sec
oscillationTargetRotationForThisDP // Passe la rotation cible calculée
);
}
else
{
Debug.LogWarning($"[LaserInfinyRotateConique] DirectionPrefab[{i}] est null ou n'a pas de préfabriqué de tube assigné. Veuillez l'assigner dans l'Inspector.");
}
}
if (!anyPrefabAssigned)
{
Debug.LogError("[LaserInfinyRotateConique] Aucun préfabriqué de tube valide assigné dans 'directionPrefabs' ! Veuillez en assigner au moins un.");
enabled = false;
return;
}
}
/// <summary>
/// Crée un nouvel objet GameObject MultiLaserSpawnerRotateConique et l'initialise.
/// </summary>
/// <param name="position">Position mondiale du nouveau spawner.</param>
/// <param name="laserIndex">Index pour choisir DirectionPrefab et l'intervalle des tableaux.</param>
/// <param name="recursionLevel">Niveau de récursion actuel du spawner.</param>
/// <param name="delay">Délai avant le début de l'apparition (non utilisé dans l'InvokeRepeating de cette version).</param>
/// <param name="enableAngularOscillationForSpawner">Indique si l'oscillation angulaire est activée pour ce spawner.</param>
/// <param name="oscillationSpeedPerSec">La vitesse angulaire de l'oscillation en degrés par seconde.</param>
/// <param name="oscillationTargetRotation">La rotation cible pour l'oscillation angulaire.</param>
public void CreateNewMultiSourceSpawner(
Vector3 position,
int laserIndex,
int recursionLevel,
float delay,
bool enableAngularOscillationForSpawner,
float oscillationSpeedPerSec,
Quaternion oscillationTargetRotation
)
{
Debug.Log($"[LaserInfinyRotateConique] Création d'un nouveau spawner multi-source au niveau {recursionLevel} pour l'index de laser {laserIndex}.");
GameObject multiSourceSphere = new GameObject($"MultiSourceSpawner_Level{recursionLevel}_Laser{laserIndex}");
multiSourceSphere.transform.position = position;
MultiLaserSpawnerRotateConique multiSourceSpawner = multiSourceSphere.AddComponent<MultiLaserSpawnerRotateConique>();
_activeMultiSourceSpawners.Add(multiSourceSpawner);
float currentInterval = (intervalSpawns != null && intervalSpawns.Length > 0) ? intervalSpawns[laserIndex % intervalSpawns.Length] : 1f;
multiSourceSpawner.Init(
this,
speed,
currentInterval,
position,
recursionLevel,
enableLongitudinalRotation,
longitudinalRotationSpeed,
enableAngularOscillationForSpawner,
oscillationSpeedPerSec,
oscillationTargetRotation
);
}
/// <summary>
/// Appelé lorsque le GameObject est désactivé ou détruit. Nettoie tous les objets enfants apparus.
/// </summary>
void OnDisable()
{
foreach (var spawner in _activeMultiSourceSpawners)
{
if (spawner != null)
{
Destroy(spawner.gameObject);
}
}
_activeMultiSourceSpawners.Clear();
}
}
lien Prefabs:
https://drive.google.com/drive/folders/ ... sp=sharing
ça fonctionne (cf image jointe): cf lien vidéo:
https://drive.google.com/file/d/1yH72xo ... sp=sharing
mais je ne comprends pas pourquoi lorsque nb_lasers>1 et quand l'oscillation angulaire est cochée,le nombre de lasers rotatifs est dupliqué !
Pouvez-vous m'aider?
merci de votre aide