using TMPro; using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using UnityEngine; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif using UnityEngine.UI; ///////////////////////////////////////////////////////////////////////////////// namespace Rukhanka.Samples { [BurstCompile] class UIController_CrowdSample: MonoBehaviour { public TextMeshProUGUI spawnCountLabel; public TextMeshProUGUI counterLabel; public TextMeshProUGUI bonesCountLabel; public TextMeshProUGUI verticesCountLabel; public Slider spawnCountSlider; public Button spawnBtn; public Button despawnBtn; public Toggle visualizeSkeletonsToggle; public TMP_Dropdown modeSelector; public SimpleCameraDollyCart cameraCart; public GameObject mmRoot; public TextMeshProUGUI mmSwitchDistanceLabel; public Slider mmSwitchDistanceSlider; public TextMeshProUGUI animatorModeLabel; EntityQuery spawnerQuery, rigsQuery, skinnedMeshQuery, visualizedRigsQuery, notVisualizedRigsQuery, gpuEntitiesQuery, cpuEntitiesQuery; EntityManager em; DistanceBasedMixedModeAnimatorSystem dbs; ///////////////////////////////////////////////////////////////////////////////// void Start() { spawnBtn.onClick.AddListener(SpawnPrefabs); despawnBtn.onClick.AddListener(DespawnEntities); var worlds = World.All; foreach (var w in worlds) { if (RukhankaSystemsBootstrap.IsClientOrLocalSimulationWorld(w)) { em = w.EntityManager; break; } } dbs = em.World.GetExistingSystemManaged(); spawnerQuery = new EntityQueryBuilder(Allocator.Temp) .WithAll() .Build(em); rigsQuery = new EntityQueryBuilder(Allocator.Temp) .WithAllRW() .Build(em); skinnedMeshQuery = new EntityQueryBuilder(Allocator.Temp) .WithAllRW() .Build(em); visualizedRigsQuery = new EntityQueryBuilder(Allocator.Temp) .WithAll() .Build(em); notVisualizedRigsQuery = new EntityQueryBuilder(Allocator.Temp) .WithAll() .WithNone() .Build(em); gpuEntitiesQuery = new EntityQueryBuilder(Allocator.Temp) .WithAll() .Build(em); cpuEntitiesQuery = new EntityQueryBuilder(Allocator.Temp) .WithDisabled() .Build(em); AnimatorModeChange(false); #if !RUKHANKA_DEBUG_INFO visualizeSkeletonsToggle.enabled = false; visualizeSkeletonsToggle.isOn = false; var tmp = visualizeSkeletonsToggle.GetComponentInChildren(); tmp.text += " (RUKHANKA_DEBUG_INFO is not defined)"; tmp.color = Color.gray; #endif } ///////////////////////////////////////////////////////////////////////////////// void SpawnPrefabs() { var spawners = spawnerQuery.ToEntityArray(Allocator.Temp); var multiplier = 1.0f; #if ENABLE_INPUT_SYSTEM multiplier = Keyboard.current.ctrlKey.isPressed ? 10 : 1; #endif foreach (var s in spawners) { var scc = new SpawnCommandComponent() { spawnCount = (int)(spawnCountSlider.value / spawners.Length * multiplier), boneVisualizationOn = visualizeSkeletonsToggle.isOn, gpuAnimator = modeSelector.value == 1 }; em.AddComponentData(s, scc); } } ///////////////////////////////////////////////////////////////////////////////// void DespawnEntities() { var rigsArr = rigsQuery.ToEntityArray(Allocator.Temp); var multiplier = 1.0f; #if ENABLE_INPUT_SYSTEM multiplier = Keyboard.current.ctrlKey.isPressed ? 10 : 1; #endif var numToDespawn = math.min(rigsArr.Length, (int)(spawnCountSlider.value * multiplier)); var allRigsIndices = new NativeList(rigsArr.Length, Allocator.Temp); var rigsToDespawn = new NativeArray(numToDespawn, Allocator.Temp); for (int i = 0; i < rigsArr.Length; ++i) { allRigsIndices.Add(i); } var rng = new Unity.Mathematics.Random((uint)numToDespawn + 1); for (var i = 0; i < rigsToDespawn.Length; ++i) { var randomIndex = rng.NextInt(allRigsIndices.Length); rigsToDespawn[i] = allRigsIndices[randomIndex]; allRigsIndices.RemoveAtSwapBack(randomIndex); } for (var i = 0; i < rigsToDespawn.Length; ++i) { em.DestroyEntity(rigsArr[rigsToDespawn[i]]); } } ///////////////////////////////////////////////////////////////////////////////// [BurstCompile] static int CalculateTotalBonesCount(ref EntityQuery eq) { var rv = 0; var rigs = eq.ToComponentDataArray(Allocator.Temp); foreach (var r in rigs) { rv += r.rigBlob.Value.bones.Length; } return rv; } ///////////////////////////////////////////////////////////////////////////////// [BurstCompile] static int CalculateTotalSkinnedVerticesCount(ref EntityQuery eq) { var rv = 0; var sms = eq.ToComponentDataArray(Allocator.Temp); foreach (var sm in sms) { rv += sm.smrInfoBlob.Value.meshVerticesCount; } return rv; } ///////////////////////////////////////////////////////////////////////////////// public void SwitchBoneVisualization(Toggle t) { if (t.isOn) { em.AddComponent(notVisualizedRigsQuery); } else { em.RemoveComponent(visualizedRigsQuery); } } ///////////////////////////////////////////////////////////////////////////////// public void AnimatorModeChange(bool switchAnimationEngine) { switch (modeSelector.value) { case 0: dbs.Enabled = false; mmRoot.SetActive(false); if (switchAnimationEngine) em.SetComponentEnabled(gpuEntitiesQuery, false); animatorModeLabel.text = "All entities use CPU animator"; break; case 1: dbs.Enabled = false; mmRoot.SetActive(false); if (switchAnimationEngine) em.SetComponentEnabled(cpuEntitiesQuery, true); animatorModeLabel.text = "All entities use GPU animator"; break; case 2: mmRoot.SetActive(true); dbs.Enabled = true; animatorModeLabel.text = "Camera distance based animator switch"; break; } } ///////////////////////////////////////////////////////////////////////////////// public void SwitchCameraCartMovement(Toggle toggle) { cameraCart.enabled = toggle.isOn; } ///////////////////////////////////////////////////////////////////////////////// void Update() { spawnCountLabel.text = $"{spawnCountSlider.value}"; var animatedObjectsCount = rigsQuery.CalculateEntityCount(); counterLabel.text = $"Total Instance Count: {animatedObjectsCount}"; var totalBonesCount = CalculateTotalBonesCount(ref rigsQuery); bonesCountLabel.text = $"Total Bone Count: {totalBonesCount}"; mmSwitchDistanceLabel.text = $"{mmSwitchDistanceSlider.value}"; if (verticesCountLabel != null) { var totalVerticesCount = CalculateTotalSkinnedVerticesCount(ref skinnedMeshQuery); verticesCountLabel.text = $"Total Skinned Vertices Count: {totalVerticesCount}"; } dbs.switchDistance = mmSwitchDistanceSlider.value; } } }