Unity disable particle system when outside of camera - unity3d

I am making a 2D game in unity that has an underwater theme. To make it look really pretty, I use a script to spawn glowy objects at random locations in the map. These objects are blank sprites with Particle Systems attached to them.
The problem is, I have about 100 of them active in the map at a time, and though it looks really pretty, the game lags on computers that don't have an i5 processor or faster.
The solution I thought of was to disable the objects while they are out of camera bounds so there is only about 4 or 5 active at a time...
Here's my script:
void Update () {
if (this.transform.position.x < Camera.main.transform.position.x + 13 && this.transform.position.x > Camera.main.transform.position.x - 13 && this.transform.position.y < Camera.main.transform.position.y + 13 && this.transform.position.y > Camera.main.transform.position.y - 13) { this.gameObject.particleEmitter.emit = true;
this.gameObject.particleSystem.Play ();
}
else {
this.gameObject.particleSystem.Pause ();
}
}
Ok, it checks if the object is outside the camera bounds correctly, but when it comes to disabling the object, I get this error:
"NullReferenceException: Object reference not set to an instance of an object"

You use both particleEmitter, which refers to the legacy particle system, and particleSystem, which refers to the new Souriken system. You're probably only using Souriken particles, so I would rewrite it like this:
void Update ()
{
if (this.transform.position.x < Camera.main.transform.position.x + 13
&& this.transform.position.x > Camera.main.transform.position.x - 13
&& this.transform.position.y < Camera.main.transform.position.y + 13
&& this.transform.position.y > Camera.main.transform.position.y - 13)
{
particleSystem.Play ();
}
else
{
particleSystem.Pause ();
}
}

Related

How do i fix the placement tool in my level editor?

So i'm currently making a game, and i've recently added a level editor, but the placing tool does not work how i wanted it to.
https://youtu.be/MuUvnVTL6eg
If you've watched this video, you've probably realized that the block placing works pretty much how placing rectangles in ms pain with alt does, and i want it to work like placing rectangles in ms pain without alt xd.
I'm using this code to place the block:
if (Input.GetKeyDown(KeyCode.Mouse0)){
startDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
tmpObj = spawnObject(blocks[selected].gameObject, startDrawPos);
drawing = true;
}
if (Input.GetKey(KeyCode.Mouse0)){
Vector2 mPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 tmpScale = new Vector2(startDrawPos.x - mPos.x, startDrawPos.y - mPos.y);
tmpObj.transform.localScale = tmpScale;
}
if (Input.GetKeyUp(KeyCode.Mouse0))
{
drawing = false;
var scale = tmpObj.transform.localScale;
//Code below destroys the object if it's too small to avoid accidental placements
if (scale.x <= 0.1 && scale.x > -0.1 || scale.y <= 0.1 && scale.y > -0.1)
{
Destroy(tmpObj);
}
}
(All of this code is in the Update() function)
(spawnObject function just instantiates the object prefab)
There is a bit more code but it has nothing to do with the position of the block, it just detect which block is selected and decides if it can be resized or not.
I solved this problem. But because your complete script is not in question, I rebuilt the code with IEnumerator, Here, by pressing the left mouse button, IEnumerator is activated and all commands are grouped in one method to make the code more efficient.
private void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0)) StartCoroutine(DrawRect());
}
How does the Desktop Rect formula work?
By running IEnumerator, the code first records the starting point of the mouse. It also makes a simple cube because I do not have access to your objects. Now until the mouse is pressed. Resize Rect to the difference between current and recorded points. The only thing is that to avoid ALT control, you have to place it between the current and initial points. The reason for adding the camera forward is to be seen in the camera.
cubeObject.transform.position = (startDrawPos + currentDrawPos) / 2;
The final structure of the DrawRect is as follows:
public IEnumerator DrawRect()
{
drawing = true;
var scale = Vector2.zero;
var startDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
var cubeObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
while (Input.GetKey(KeyCode.Mouse0))
{
var currentDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
cubeObject.transform.position = (startDrawPos + currentDrawPos) / 2 + Camera.main.transform.forward * 10;
scale = new Vector2(startDrawPos.x - currentDrawPos.x, startDrawPos.y - currentDrawPos.y);
cubeObject.transform.localScale = scale;
yield return new WaitForEndOfFrame();
}
if (scale.x <= 0.1 && scale.x > -0.1 || scale.y <= 0.1 && scale.y > -0.1) Destroy(cubeObject);
drawing = false;
}

Unity3d - Collision detection and position inaccuracy

I'm a beginner on Unity3D and I decided to create a 3D Tetris to start learning. Everything was fine so far, but I have a problem with collision detection.
https://gyazo.com/49fbf5798dc67a546c5e187fde8f6096
As you can see on this screen, the blue tetromino is not at the good place. This is due to the fact that the orange tetromino position in X is not precise (-2.9999).
Each block that makes up a tetromino has the tag "Block". The ground has the tag "Ground".
This is how I detect collisions in my OnCollisionEnter method.
if (collision.collider.tag == "Block" && !detectedBefore)
{
for (int k = 0; k < collision.contacts.Length; k++)
{
if (Vector3.Angle(collision.contacts[k].normal, validDirection) <= contactThreshold)
{
GetComponent<TetrominoMovement>().Snap();
GetComponent<TetrominoMovement>().enabled = false;
FindObjectOfType<Tetromino>().GenTetromino();
detectedBefore = true;
break;
}
}
}
if (collision.collider.tag == "Ground" && !detectedBefore)
{
GetComponent<TetrominoMovement>().Snap();
GetComponent<TetrominoMovement>().enabled = false;
FindObjectOfType<Tetromino>().GenTetromino();
detectedBefore = true;
}
Do you know how to get around this problem? Thank you in advance.

How to use boolean functions to display different "menus" of processing game

I am writing code for a version of "Flappy Bird". I am trying to produce a start menu, end menu, and difficulty menu. So far I have been successful with the start and end, but am struggling with the difficulty menu to show. I believe my code is also very messy and would like to tidy it up, but not sure where to start.
I tried adding boolean "diff" and including that as false in my arguments in the mousePressed method.
void mousePressed() {
//START MENU
if (menu==true&&end==false) {
if (mouseX > 175 && mouseX < 625 && mouseY > 500 && mouseY < 600) {
menu = false;
end = true;
}
if (mouseX > 175 && mouseX < 625 && mouseY > 650 && mouseY < 750) {
diff();
}
}
//GAMEPLAY
if (menu==false&&end==true) {
b.bounce();
}
//END MENU
if (menu==false&&end==false) {
if (mouseX > 200 && mouseX < 600 && mouseY > 500 && mouseY < 600) {
restart();
b.bounce();
}
if (mouseX > 200 && mouseX < 600 && mouseY > 650 && mouseY < 750) {
startMenu();
}
}
}
I expected the output to show the difficulty menu, but it only showed the difficulty menu for one frame, then snapped back to the start menu.
I'm assuming your diff() function draws the difficulty menu. I'm also assuming you have a draw() function that draws things depending on your game's current state (menu, game, end menu, etc...).
The problem is that you're drawing your difficulty menu once, when the mouse is pressed on the difficulty button, and then it's being immediately overdrawn by draw().
In programs that draw continuously (like with draw()), whenever you want to show something for more than one frame, you need to draw it each frame (i.e. in draw()).
The solution is to add a "difficulty menu" state. Your draw() function would then work something like this:
void draw() {
if (menu && !diff && !end) {
drawMenu();
} else if (!menu && diff && !end) {
diff();
} else if (!menu && !diff && end) {
//Why do you draw the game when end is TRUE?
drawGame();
} else if (!menu && !diff && !end) {
//Why do you draw the end menu when end is FALSE?
drawEndMenu();
}
}
You would, of course, need to modify the mousePressed() function as well.
You also said that you feel your code is messy. This is a type of code smell; Whenever somethings feels "wrong" when you're programming, it often means there's a better way to do it. It's good that you noticed this.
Firstly, your function names are a bit non-descriptive: Instead of diff(), I recommend something like drawDiffMenu(). Rule of thumb: Function names should almost always be verbs.
Secondly, you shouldn't be handling gamestate with a bunch of booleans; Since you can only have one state at a time, you should only have one variable. (If you ever need a state-inside-a-state, make another variable.) Best way to do that is with enums!
Example:
enum GameState {
MENU, GAME, DIFF_MENU, END
}
GameState gameState = GameState.MENU;
//...
void draw() {
// Google "java switch statement"
if (gameState == GameState.MENU) {
drawMenu();
} else if (gameState == GamesState.DIFF_MENU) {
drawDiffMenu();
} else if...
// Etc...
}
A nitpick: Calling b.bounce() after restart() is a bit odd, since in theory that bounce should be a part of your game starting.
Happy coding!

Unity - CheckSphere not working? spawn freezes only on device?

I am trying to spawn a set number of cubes within an ever changing area (a plane, ARKit) and NOT have them overlap. Pretty simple I'd think, and I have this working in Unity editor like so:
My problem is deploy to device (iPhone) and everything is different. Several things aren't working, and I don't know why - it's a relatively simple script. First I thought CheckSphere wasn't working, something with scale being different - but this is how I try to get an empty space:
public Vector3 CheckForEmptySpace (Bounds bounds)
{
float sphereRadius = tierDist;
Vector3 startingPos = new Vector3 (UnityEngine.Random.Range(bounds.min.x, bounds.max.x), bounds.min.y, UnityEngine.Random.Range(bounds.min.z, bounds.max.z));
// Loop, until empty adjacent space is found
var spawnPos = startingPos;
while ( true )
{
if (!(Physics.CheckSphere(spawnPos, sphereRadius, 1 << 0)) ) // Check if area is empty
return spawnPos; // Return location
else
{
// Not empty, so gradually move position down. If we hit the boundary edge, move and start again from the opposite edge.
var shiftAmount = 0.5f;
spawnPos.z -= shiftAmount;
if ( spawnPos.z < bounds.min.z )
{
spawnPos.z = bounds.max.z;
spawnPos.x += shiftAmount;
if ( spawnPos.x > bounds.max.x )
spawnPos.x = bounds.min.x;
}
// If we reach back to a close radius of the starting point, then we didn't find any empty spots
var proximity = (spawnPos - startingPos).sqrMagnitude;
var range = shiftAmount-0.1; // Slight 0.1 buffer so it ignores our initial proximity to the start point
if ( proximity < range*range ) // Square the range
{
Debug.Log( "An empty location could not be found" );
return new Vector3 (200, 200, 200);
}
}
}
}
This again, works perfect in editor. This is the code Im running on my device (without check sphere)
public void spawnAllTiers(int maxNum)
{
if(GameController.trackingReady && !hasTriedSpawn)
{
hasTriedSpawn = true;
int numTimesTried = 0;
BoxCollider bounds = GetGrid ();
if (bounds != null) {
while (tiersSpawned.Length < maxNum && numTimesTried < 70) { //still has space
Tier t = getNextTier ();
Vector3 newPos = new Vector3 (UnityEngine.Random.Range(GetGrid ().bounds.min.x, GetGrid ().bounds.max.x), GetGrid ().bounds.min.y, UnityEngine.Random.Range(GetGrid ().bounds.min.z, GetGrid ().bounds.max.z));
//Vector3 newPos = CheckForEmptySpace (bounds.bounds);
if(GetGrid ().bounds.Contains(newPos)) //meaning not 200 so it is there
{
spawnTier (newPos, t);
}
numTimesTried++;
platformsSpawned = GameObject.FindObjectsOfType<Platform> ();
tiersSpawned = GameObject.FindObjectsOfType<Tier> ();
}
if(tiersSpawned.Length < maxNum)
{
print ("DIDNT REACH - maxed at "+tiersSpawned.Length);
}
}
}
//maybe check for num times trying, or if size of all spawned tiers is greater than area approx
}
//SPAWN NEXT TIER
public void spawnTier(Vector3 position, Tier t) //if run out of plats THEN we spawn up like tree house
{
print ("SUCCESS - spawn "+position+"SPHERE: "+Physics.CheckSphere(position, tierDist, 1 << 0));
// Vector3 pos = currentTier.transform.position; //LATER UNCOMMENT - would be the current tier spawning from
//TO TEST comment to this line ---------------------------------------------------------------------------
#if UNITY_EDITOR
Instantiate (t, position, Quaternion.identity);
anchorManager.AddAnchor(t.gameObject);
#else
//------------------------------------------------------------------------------------------
Instantiate (t, position, Quaternion.identity);
anchorManager.AddAnchor(t.gameObject);
#endif
}
This doesnt crash the device but spawns ALL in the same place. I cant understand why. If I do this, CHECKING for overlap:
public void spawnAllTiers(int maxNum)
{
if(GameController.trackingReady && !hasTriedSpawn)
{
hasTriedSpawn = true;
int numTimesTried = 0;
BoxCollider bounds = GetGrid ();
if (bounds != null) {
while (tiersSpawned.Length < maxNum && numTimesTried < 70) { //still has space
Tier t = getNextTier ();
//Vector3 newPos = new Vector3 (UnityEngine.Random.Range(GetGrid ().bounds.min.x, GetGrid ().bounds.max.x), GetGrid ().bounds.min.y, UnityEngine.Random.Range(GetGrid ().bounds.min.z, GetGrid ().bounds.max.z));
Vector3 newPos = CheckForEmptySpace (GetGrid ().bounds);
if(GetGrid ().bounds.Contains(newPos) && t) //meaning not 200 so it is there
{
spawnTier (newPos, t);
}
numTimesTried++;
platformsSpawned = GameObject.FindObjectsOfType<Platform> ();
tiersSpawned = GameObject.FindObjectsOfType<Tier> ();
}
if(tiersSpawned.Length < maxNum)
{
print ("DIDNT REACH - maxed at "+tiersSpawned.Length);
}
}
}
//maybe check for num times trying, or if size of all spawned tiers is greater than area approx
}
Works great in editor again, but completely freezes the device. Logs are not helpful, as I just get this every time, even though they aren't spawning in those positions:
SUCCESS - spawn (0.2, -0.9, -0.9)SPHERE: False
SUCCESS - spawn (-0.4, -0.9, 0.2)SPHERE: False
SUCCESS - spawn (0.8, -0.9, 0.2)SPHERE: False
SUCCESS - spawn (-0.4, -0.9, -0.8)SPHERE: False
SUCCESS - spawn (0.9, -0.9, -0.8)SPHERE: False
What the hell is happening - why would it freeze only on device like this?
Summary:
it sounds like you needed a short gap between each spawn.
(BTW a useful trick is, learn how to wait until the next frame - check out many articles on it.)
All-time classic answer for this
https://stackoverflow.com/a/35228592/294884
get in to "chunking" for random algorthims
observe the handy line of code at "How to get sets of unique random numbers."
Enjoy
Unrelated issue -
Could it be you need to basically wait a small moment between spawning each cube?
For a time in unity it's very simply Invoke - your code pattern would look something like this:
Currently ...
for 1 to 100 .. spawn a cube
To have a pause between each ...
In Start ...
Call Invoke("_spawn", 1f)
and then
func _spawn() {
if count > 70 .. break
spawn a cube
Invoke("_spawn", 1f)
}
Similar example code - https://stackoverflow.com/a/36736807/294884
Even simpler - https://stackoverflow.com/a/35807346/294884
Enjoy

Dodging-code uncertainty (Unity, no Unity physics)

So, I want to implement dodging which is something like a dash in a direction. It works pretty simple - if you move in a direction and press the button the character dodges there. The velocity should not matter for this, so that the dodging speed is always the same, no matter if you are running or standing and then pressing a direction + dodging. I've got something that seems to work but there are still problems left. It seems like that diagonal dodging is a bit too far in comparison to horizontal/vertical dodging and I tried to fix this. But I'm still not sure if it worked or not. In practice the character dodges and stuff but something still seems off, like it works pretty good with a controller but sometimes it's still wacky when using keyboard after standing still and then using dodge. The thing is - I'm not sure if the code I have is the best I could have or if there are still problems.
A good example of how it should work would be the dodging in Rune Factory 4.
Edit: If the problem lies in the Input Settings I could show them.
if (IsDodging == false)
{
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
if (input.sqrMagnitude > 1) input.Normalize();
if (Input.GetButtonDown("Jump"))
{
// Jumping stuff I blended out
}
else if (Input.GetButtonDown("Block/Dodge") && input.x != 0 || Input.GetButtonDown("Block/Dodge") && input.z != 0) // To activate dodging
{
if (Time.time > Time_Start + DodgeCooldown) // Checks the cooldown
{
Time_Start = Time.time;
IsDodging = true;
dodge_direction = input;
if (dodge_direction.x > 0) { dodge_direction.x = 1; } // To fixate the distance traveled when dodging while ignoring how fast you are
if (dodge_direction.z > 0) { dodge_direction.z = 1; }
if (dodge_direction.x > 0 && dodge_direction.z > 0) { dodge_direction.Normalize(); } // To prevent diagonal dodging distance from becoming too big
}
}
else
{
// Walk.
transform.position += input * run_speed * Time.deltaTime;
}
}
else
{
transform.position += dodge_direction * dodge_distance;
distance_traveled += Vector3.Distance(transform.position, dodge_direction * dodge_distance) * Time.deltaTime;
if (distance_traveled > dodge_distance)
{
IsDodging = false;
dodge_direction = Vector3.zero;
distance_traveled = 0;
}
}