How to control particles directly while preventing the particle system from doing the same? - unity3d

I have lots of same simple objects that are affecting gameplay, thousands of them! Well, not thousands, but really many. So if I make them GameObjects, the FPS decreases, especially when spawning them. Even with pooling. I should try a different approach.
You know that particle system in Unity3D can render many particles very fast. It also automatically controls particles, emits and removes them. But in my case, positions and lifetimes of objects are managed by game logic, and particle system is not allowed to do anything without my command, even reorder particles.
I am trying to use SetParticles method to control particles. It works in test project, where I use GetParticles first. I can even remove particles setting lifetime to -1, but can't spawn new ones. Also it does not prevent particle system from controlling particles.
I can disable emission so no particles will be created automatically.
I can set particles speed to 0 so they will not move.
I can set lifetime to huge number so they will not be removed.
I have an pool of Particle instances, to avoid unnessessary GC allocations. Objects receive a reference to particle when they are spawned, change it when updated, and set lifetime -1 and return it to pool when deleted. The pool stores this:
private ParticleSystem.Particle [] _unusedParticles;
private int _unusedCount;
private ParticleSystem.Particle [] _array;
_unused array and counter are needed for pooling, and _array stores all particles, used and unused alike, and is used in SetParticles call.
The main disadvantage of this method is that it doesn't work, probably because SetParticles does not create new particles. Also I guess it doesn't do anything with particles draw order, that's why it's poorly suited for bullet hell games where bullet patterns should look nice.
What should I do to properly disable automatic control of particles and properly setup direct control, with spawning and removing?

What you are looking for might be
List<Matrix4x4> matrixes=new List<Matrix4x4>();
for (...)
{
matrixes.Add(Matrix4x4.TRS( position,rotation,scale));
}
Graphics.DrawMeshInstanced(mesh,0,material, matrixes);
On each frame you can just update positions, rotations and scales, and get all the instances rendered on the GPU in one drawcall (pretty darn fast compared to seperate gameobjects). You can render up to 1000 instances in one call using this way

Create an empty GameObject, then add the ParticleSystem as child. Set playOnAwake to true.
Now when you need it, set GameObject.SetActive to true else false.
To get each of them use ParticleSystem.GetParticles, modify them and ParticleSystem.GetParticles.
I hope this is what you are looking for.
ParticleSystem m_System;
ParticleSystem.Particle[] m_Particles;
public float m_Drift = 0.01f;
private void LateUpdate()
{
InitializeIfNeeded();
// GetParticles is allocation free because we reuse the m_Particles buffer between updates
int numParticlesAlive = m_System.GetParticles(m_Particles);
// Change only the particles that are alive
for (int i = 0; i < numParticlesAlive; i++)
{
m_Particles[i].velocity += Vector3.up * m_Drift;
}
// Apply the particle changes to the Particle System
m_System.SetParticles(m_Particles, numParticlesAlive);
}
void InitializeIfNeeded()
{
if (m_System == null)
m_System = GetComponent<ParticleSystem>();
if (m_Particles == null || m_Particles.Length < m_System.main.maxParticles)
m_Particles = new ParticleSystem.Particle[m_System.main.maxParticles];
}

After we created a particle system in editor, we should disable emission and shape, so only core part and renderer stay active.
The most important part is that Simulation Speed must be zero. A particle system will no longer emit, remove or process particles automatically. Only your code now manages them.
I use this class to control particles. Instead of binding a particle to an object, it has API for registering objects, smoke in this case. Also, it stores a temporary array for particles to avoid GC allocations, and particle count, to avoid using particleCount property of the particle system.
In Update, which is called by my game logic, the following happens:
All objects that were despawned by game logic, are removed from list.
If object count is greater than array length, the array is resized.
If object count is greater than live particle count, _particleSystem.Emit call is made. If we call SetParticles without that, no new particles will appear. We must emit them first.
GetParticles is called. Simple, but probably not the most efficient solution, but it preserves particles data. It may be optimized by setting all particle data on array creation and resizing. If you do so, remove GetParticles call and uncomment the line above. Also, your game logic should manage particles even more carefully.
For each object, we let that object change a particle.
Each particle without object should be removed, so their lifetime is set to negative number.
SetParticles updates particles in system.
public class SmokeSystem {
private ParticleSystem _particleSystem;
private List <Smoke> _smoke = new List <Smoke> ();
private ParticleSystem.Particle [] _particles = new ParticleSystem.Particle[256];
private int _particleCount;
public SmokeSystem (ParticleSystem particleSystem) {
_particleSystem = particleSystem;
}
public void AddSmoke (Smoke smoke) => _smoke.Add (smoke);
public void Update () {
_smoke.RemoveAll (e => e.Despawned);
if (_smoke.Count > _particles.Length) {
int newSize = Max (_smoke.Count, 2 * _particles.Length);
Array.Resize (ref _particles, newSize);
}
int count = _smoke.Count;
if (count > _particleCount) {
_particleSystem.Emit (count - _particleCount);
// _particleCount = count;
}
_particleCount = _particleSystem.GetParticles (_particles);
for (int i = 0; i < count; i++) {
_smoke [i].UpdateParticle (ref _particles [i]);
}
for (int i = count; i < _particleCount; i++) {
_particles [i].remainingLifetime = -1;
}
_particleSystem.SetParticles (_particles, _particleCount);
_particleCount = count;
}
}
It does not depend on GPU instancing support so it will work on WebGL.

Related

How to make Trajectory Predictor on my ball?

hey guys , So as you can see i made a robot arm grab a slingshot's objectholder with a ball in it. My arm pulls it any direction I want it but I wanted the user to know which box is going to be shot at.
If you're applying an impulse force (or velocity) to your ball and there is gravity in your world, your item will follow the Projectile motion;
Here you can find details about it:
https://en.wikipedia.org/wiki/Projectile_motion
There are basically two main options
calculating it yourself
This is probably way better for performance especially if you want a really simple trajectory preview without accounting for any collision etc
refer to linked article. But it basically comes down to
and would need slightly rework from 2D to 3D physics, should be trivial though since the important part about the Y axis basically stays the same.
You would
Call this simulation with according supposed shoot direction and velocity
Visualize the tracked positions e.g. in a LineRenderer
Physics.Simulate
This allows you to run physics updates manually all within a single frame and actually let the Physics engine handle it all for you
This costs of course a lot of performance but you get all collisions etc accounted for automatically without getting a headache
You would
Make a snapshot of all Rigid bodies in your scene - in order to reset after the simulation
Simulate the desired amount of physics steps (XY seconds ahead) while keeping track of the simulated data
reset everything to the state tracked in step 1
use the simulated data from step 2 to visualize e.g. with a LineRenderer
This might look somewhat like e.g.
public class Prediction : MonoBehaviour
{
public LineRenderer line;
public Rigidbody tracked;
private Rigidbody[] allRigidbodies;
private void Awake()
{
allRigidbodies = FindObjectsOfType<Rigidbody>();
}
private void LateUpdate()
{
// Wherever you would get this from
Vector3 wouldApplyForce;
// Step 1 - snapshot
// For simplicity reasons for now just the positions
// using some Linq magic
var originalPositions = allRigidbodies.ToDictionary(item => item, item => item.position);
// Step 2 - Simulate e.g. 2 seconds ahead
var trackedPositions = new Vector3 [(int) (2 / Time.fixedDeltaTime)];
Physics.autoSimulation = false;
tracked.AddForce(wouldApplyForce);
for(var i = 0; i < trackedPositions.Length; i++)
{
Physics.Simulate(Time.fixedDeltaTime);
trackedPositions[i] = tracked.position;
}
// Step 3 - reset
foreach (var kvp in originalPositions)
{
kvp.Key.position = kvp.Value;
}
Physics.autoSimulate = true;
// Step 4 - Visualize
line.positionCount = trackedPositions.Length;
line.SetPositions(trackedPositions);
}
}
Of course we won't talk about performance here ^^

Best way to create a 2d top down race track procedurally

I am attempting to create a 2d top-down car racing game. This game will have a random road map each time the player plays the game. I have thought about doing this in two different ways: A tilemap, or just generate the roads by placing different prefabs (straight roads, turns, etc). I have decided to go with the prefab route.
The way I believe it should work is to have prefab square "tiles" which have their own colliders set on the edges so I can tell if a player goes off the track in which case they blow up. I would have a MapGenerator Script which will generate an initial random map by keeping track of the last tile placed (including its location and road type: left turn, straight, right, etc). This script will then keep adding onto the road randomly as the player gets closer and closer to the end which makes it an infinite road.
I just want to know if this is just not efficient or if I am thinking of this completely wrong.
Here are a couple of images showing my road tiles which I made in photoshop and then one prefab for a straight road (take note of the colliders on its edges).
A similar game to one I want to make is Sling Drift which I can provide the link if you want. I don't know the policy on adding links to forum chat.
Also, here is my code for the map generator:
//Type of tyle, types are normal (straight road or horizontal road) and turns
public enum MapTileType
{
NORMAL,
N_E,
N_W,
S_E,
S_W
}
//structure for holding the last tile location and its type.
public struct TypedTileLocation
{
public TypedTileLocation(Vector2 pos, MapTileType tyleType)
{
m_tileType = tyleType;
m_position = pos;
}
public Vector2 m_position;
public MapTileType m_tileType;
}
public class MapGenerator : MonoBehaviour
{
//Map Tiles
public GameObject m_roadTile;
public GameObject m_turnNorthWestTile;
//holds all the tiles made in the game
private List<GameObject> m_allTiles;
//Map Tile Widths and Height
private float m_roadTileWidth, m_roadTileHeight;
//Used for generating next tile
TypedTileLocation m_lastTilePlaced;
private void Awake()
{
//store the initial beginning tile location (0,0)
m_lastTilePlaced = new TypedTileLocation(new Vector2(0,0), MapTileType.NORMAL);
//set height and width of tiles
m_roadTileWidth = m_roadTile.GetComponent<Renderer>().bounds.size.x;
m_roadTileHeight = m_roadTile.GetComponent<Renderer>().bounds.size.y;
m_allTiles = new List<GameObject>();
}
// Start is called before the first frame update
void Start()
{
SetupMap();
}
void SetupMap()
{
//starting at the beginning, just put a few tiles in straight before any turns occur
for (int i = 0; i < 6; ++i)
{
GameObject newTempTile = Instantiate(m_roadTile, new Vector2(0, m_roadTileHeight * i), Quaternion.identity);
m_lastTilePlaced.m_tileType = MapTileType.NORMAL;
m_lastTilePlaced.m_position.x = newTempTile.transform.position.x;
m_lastTilePlaced.m_position.y = newTempTile.transform.position.y;
m_allTiles.Add(newTempTile);
}
//now lets create a starter map of 100 road tiles (including turns and straigt-aways)
for (int i = 0; i < 100; ++i)
{
//first check if its time to create a turn. Maybe I'll randomly choose to either create a turn or not here
//draw either turn or straight road, if the tile was a turn decide which direction we are now going (N, W, E, S).
//this helps us determine which turns we can take next
//repeat this process.
}
}
void GenerateMoreMap()
{
//this will generate more map onto the already existing road and then will delete some of the others
}
// Update is called once per frame
void Update()
{
}
private void OnDrawGizmos()
{
}
}
Thanks!
Have you tried splines? They let you make curvy paths like race tracks easily. If not, here is a video that might help: https://www.youtube.com/watch?v=7j_BNf9s0jM.

Instantiate GameObjects and change Material when it hits on the ground

There is a code for instantiate cube into the list and change a Material of each clone when it hits on the ground
The following code works but not in Real-Time. Update function works like a Start function for a Foreach method
How to get a value of item.transform.position.y in the Update function Real-Time?
public GameObject cubePrefab;
public Material RedMat;
public float GroudLevel = 0.5f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
List<GameObject> cloneList = new List<GameObject>();
//instantiate clones into the list
for (int i = 0; i < 10; i++)
{
var clone = Instantiate (cubePrefab,new Vector3(Random.Range(-3f, 3f),
Random.Range(4f, 10.5f),Random.Range(-3f, 3f)), Quaternion.identity);
cloneList.Add(clone);
}
//if clone is grounded change a Material for each clone
foreach (var item in cloneList)
{
//Debug.Log(item.transform.position.y);
//check if clone is on the ground
if(item.transform.position.y < GroudLevel)
{
item.GetComponent<Renderer>().material = RedMat;
}
}
}
}
There is a screenshot for a GroudLevel = 7
The reason this function isn't working is because the pivot (transform.position) of your item is always in the center of the object. This isn't something you can change in Unity (nor would you necessarily want to).
On top of this, you're checking if the item is under the ground, and not on it when you use < insteaad of <=, because the position needs to be less than groundLevel to return true.
There are several solutions here.
The simplest would involve moving all of this logic to an OnCollisionEnter or OnTriggerEnter method. For more information on this, check the Unity documentation.
Another solution would be to find a way tthe size of the object, divide it by two, and check if
item.transform.position - halfSize <= groundLevel;
This seems really cumbersome and overly complex, however. You'd be better off using Unity's built-in collision system, unless you have a reason not to.

How to properly use movement components in unreal engine 4?

I'm trying to set up a very simple RTS-like camera that moves around at the pressure of wasd or the arrow keys. But I can't manage to make it work.
I have a player controller that handles input like so:
void AMyPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAxis("CameraForward", this, &AMyPlayerController::CameraForward);
}
void AMyPlayerController::CameraForward(float Amount)
{
MyRtsCameraReference->CameraForward(Amount);
}
In my camera actor, which inherits from APawn, and I initialize like this:
ARtsCamera::ARtsCamera()
{
PrimaryActorTick.bCanEverTick = true;
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
// I tried to use a static mesh in place of this but nothing changes.
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
MovementComponent = CreateDefaultSubobject<UFloatingPawnMovement>(TEXT("MovementComponent"));
SpringArm->TargetArmLength = 500.f;
SetRootComponent(SceneRoot);
SpringArm->SetupAttachment(SceneRoot);
CameraComponent->SetupAttachment(SpringArm);
SpringArm->AddLocalRotation(FRotator(0.f, -50.f, 0.f));
}
I try to handle the movement like this:
void ARtsCamera::CameraForward(float Amount)
{
if (Amount != 0)
{
// Here speed is a float used to control the actual speed of this movement.
// If Amount is printed here, I get the correct value of 1/-1 at the pressure of w/s.
MovementComponent->AddInputVector(GetActorForwardVector() * Amount * Speed);
}
}
In my set up i then create a blueprint which inherits from this to expose to the level design phase a few parameters like Speed or the spring arm length and so on, but this shouldn't be the issue as if I use the C++ class the behaviour is the same.
The following works as expected (except for the fact that when i then rotate the camera, vectors get messed up) but i would like to use a movement component.
void ARtsCamera::CameraForward(float Amount)
{
if (Amount != 0)
{
AddActorWorldOffset(GetActorForwardVector() * Amount * CameraMovingSpeed);
}
}
The mobility is set to movable. And so far everything seems to be simple enough, no compile errors, but the actor doesn't move. What am I missing?
Also would you use the FloatingPawnMovement for this or would you go for another movement component?
Thanks in advance.

Spawn sprites on action

I'm trying to make a Pinata GameObject, that when clicked bursts and gives a variable number of Gift GameObjects with various images and behaviors in them.
I'm also not sure what the unity vocabulary for this is so as to look this up in unity docs.
Can anyone please lend me a hand here? Thanks!
There are several ways to handle this.
The simple way is to use Object.Instantiate, Object Instantiation is the vocab you're after.
This will create a copy of a predefined Unity object, this can be a gameobject or any other object derived from UnityEngine.Object, check the docs for more info https://docs.unity3d.com/ScriptReference/Object.Instantiate.html.
In your case, your Pinata would have an array, or list, of prefabs. These prefabs are created by you with a certain behaviour and sprite for each one. When the Pinata bursts, you instantiate random prefabs at random positions surrounding the Pinata, up to you how to position these objects.
Something along these lines should do the trick:
class Pinata : Monobehaviour
{
public GameObject[] pickupPrefabs;
public int numberOfItemsToSpawn; //This can be random
//any other variables that influence spawning
//Other methods
public void Burst()
{
for(int i = 0; i < numberOfItemsToSpawn; i++)
{
//Length - 1 because the range is inclusive, may return
//the length of the array otherwise, and throw exceptions
int randomItem = Random.Range(0, pickupPrefabs.Length - 1);
GameObject pickup = (GameObject)Instantiate(pickupPrefabs[randomItem]);
pickup.transform.position = transform.position;
//the position can be randomised, you can also do other cool effects like apply an explosive force or something
}
}
}
Bare in mind, if you want the game to be consistent, then each behaviour prefab would have there own predefined sprite, this would not be randomised. The only thing randomised would be the spawning and positioning.
If you did want to randomise the sprites for the behaviours then you'd have to add this to the Pinata class:
public class Pinata : Monobehaviour
{
//An array of all possible sprites
public Sprite[] objectSprites;
public void Burst()
{
//the stuff I mentioned earlier
int randomSprite = Random.Range(0, objectSprites.Length - 1);
SpriteRenderer renderer = pickup.GetComponent<SpriteRenderer>();
//Set the sprite of the renderer to a random one
renderer.sprite = objectSprites[randomSprite];
float flip = Random.value;
//not essential, but can make it more random
if(flip > 0.5)
{
renderer.flipX = true;
}
}
}
You can use Unity random for all your random needs, https://docs.unity3d.com/ScriptReference/Random.html
Hopefully this'll lead you in the right direction.