// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using Animancer.Units; using System.Collections.Generic; using UnityEngine; namespace Animancer.Samples.InverseKinematics { /// Spawns a bunch of obstacles and randomises them each time the target moves too far away. /// /// /// Sample: /// /// Uneven Ground /// /// /// https://kybernetik.com.au/animancer/api/Animancer.Samples.InverseKinematics/ObstacleTreadmill /// [AddComponentMenu(Strings.SamplesMenuPrefix + "Inverse Kinematics - Obstacle Treadmill")] [AnimancerHelpUrl(typeof(ObstacleTreadmill))] public class ObstacleTreadmill : MonoBehaviour { /************************************************************************************************************************/ #if UNITY_PHYSICS_3D /************************************************************************************************************************/ [SerializeField] private float _SpawnCount = 20; [SerializeField] private Material _ObstacleMaterial; [SerializeField, Meters] private float _Length = 6; [SerializeField, Degrees] private float _RotationVariance = 45; [SerializeField, Multiplier] private float _BaseScale = 0.8f; [SerializeField, Multiplier] private float _ScaleVariance = 0.3f; [SerializeField] private Transform _Target; /************************************************************************************************************************/ private readonly List Obstacles = new(); /************************************************************************************************************************/ protected virtual void Awake() { // Spawn a bunch of obstacles and randomize their layout. for (int i = 0; i < _SpawnCount; i++) { Transform capsule = GameObject.CreatePrimitive(PrimitiveType.Capsule).transform; capsule.GetComponent().sharedMaterial = _ObstacleMaterial; capsule.parent = transform; Obstacles.Add(capsule); } ScrambleObjects(); } /************************************************************************************************************************/ private void ScrambleObjects() { // Move and rotate each of the obstacles randomly. for (int i = 0; i < Obstacles.Count; i++) { Transform obstacle = Obstacles[i]; obstacle.SetLocalPositionAndRotation( new Vector3( Random.Range(0, _Length), 0, 0), Quaternion.Euler( 90, Random.Range(-_RotationVariance, _RotationVariance), 0)); float size = _BaseScale + Random.Range(-_ScaleVariance, _ScaleVariance); obstacle.localScale = Vector3.one * size; } } /************************************************************************************************************************/ protected virtual void FixedUpdate() { // When the target moves too far, teleport them back and randomize the obstacles again. Vector3 position = _Target.position; if (position.x < transform.position.x) { ScrambleObjects(); position.x += _Length; // Adjust the height to make sure it's above the ground. position.y += 5; if (Physics.Raycast(position, Vector3.down, out RaycastHit raycastHit, 10)) position = raycastHit.point; _Target.position = position; } } /************************************************************************************************************************/ [SerializeField] private Transform _Ground; // Set by a UI Slider. public float Slope { get => _Ground.localEulerAngles.z; set => _Ground.localEulerAngles = new Vector3(0, 0, value); } /************************************************************************************************************************/ #else /************************************************************************************************************************/ protected virtual void Awake() { SampleReadMe.LogMissingPhysics3DModuleError(this); } /************************************************************************************************************************/ #endif /************************************************************************************************************************/ } }